package org.deng.learn.concurrent.threadpool;

import java.util.concurrent.*;

/**
 * @author 邓力宾
 * @date 2020/5/15 下午4:49
 * @desc jdk 线程池
 */
public class JdkThreadPoolDemo {

    /**
     * 1、固定线程数的线程池（newFixedThreadPool）
     *  FixedThreadPool 是通过 java.util.concurrent.Executors 创建的 ThreadPoolExecutor 实例。这个实例会复用 固定数量的线程处理
     *  一个共享的无边界队列 。任何时间点，最多有 nThreads 个线程会处于活动状态执行任务。如果当所有线程都是活动时，有多的任务被提交过来，
     *  那么它会一致在队列中等待直到有线程可用。如果任何线程在执行过程中因为错误而中止，新的线程会替代它的位置来执行后续的任务。
     *  所有线程都会一致存于线程池中，直到显式的执行 ExecutorService.shutdown() 关闭。由于阻塞队列使用了LinkedBlockingQueue，
     *  是一个无界队列，因此永远不可能拒绝任务。LinkedBlockingQueue在入队列和出队列时使用的是不同的Lock，意味着他们之间不存在互斥关系，
     *  在多CPU情况下，他们能正在在同一时刻既消费，又生产，真正做到并行。因此这种线程池不会拒绝任务，而且不会开辟新的线程，也不会因为线程
     *  的长时间不使用而销毁线程。这是典型的生产者----消费者问题，这种线程池适合用在稳定且固定的并发场景，比如服务器。
     *  下面代码给出一个固定线程数的DEMO，每个核绑定了5个线程。
     */
    public  static void newFixedThreadPool(){
        //获取计算机的核心数
        int processors = Runtime.getRuntime().availableProcessors();
        System.out.println("核心数："+processors);
        // 第一种线程池:固定个数的线程池,可以为每个CPU核绑定一定数量的线程数
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(processors * 5);
        for (int i = 0; i < 10; i++) {
            fixedThreadPool.execute(()->{
                System.out.println(Thread.currentThread().getName());
            });
        }
        fixedThreadPool.shutdown();
    }

    /**
     * 2、缓存的线程池（newCachedThreadPool）
     *  核心池大小为0，线程池最大线程数目为最大整型，这意味着所有的任务一提交就会加入到阻塞队列中。当线程池中的线程60s没有执行任务就终止，
     *  阻塞队列为SynchronousQueue。SynchronousQueue的take操作需要put操作等待，put操作需要take操作等待，否则会阻塞（线程池的阻塞队列不能存储，
     *  所以当目前线程处理忙碌状态时，所以开辟新的线程来处理请求），线程进入wait set。
     *  总结下来：
     *  ①这是一个可以无限扩大的线程池；
     *  ②适合处理执行时间比较小的任务；
     *  ③线程空闲时间超过60s就会被杀死，所以长时间处于空闲状态的时候，这种线程池几乎不占用资源；
     *  ④阻塞队列没有存储空间，只要请求到来，就必须找到一条空闲线程去处理这个请求，找不到则在线程池新开辟一条线程。
     *  如果主线程提交任务的速度远远大于CachedThreadPool的处理速度，则CachedThreadPool会不断地创建新线程来执行任务，
     *  这样有可能会导致系统耗尽CPU和内存资源，所以在使用该线程池是，一定要注意控制并发的任务数，否则创建大量的线程可能导致严重的性能问题
     */
    public static  void newCachedThreadPool(){
        // 缓存线程池，无上限
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        for (int i = 0; i < 100; i++) {
            cachedThreadPool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                }
            });
        }
        cachedThreadPool.shutdown();
    }

    /**
     * 3、单个线程的线程池（newSingleThreadExecutor）
     * SingleThreadExecutor是使用单个worker线程的Executor，作为单一worker线程的线程池，SingleThreadExecutor把corePool
     * 和maximumPoolSize均被设置为1，和FixedThreadPool一样使用的是无界队列LinkedBlockingQueue,所以带来的影响和FixedThreadPool一样。
     * 对于newSingleThreadExecutor()来说，也是当线程运行时抛出异常的时候会有新的线程加入线程池替他完成接下来的任务。
     * 创建一个单线程化的线程池，它只会用唯一的工作线程来执行任务，
     *  保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行，
     * 所以这个比较适合那些需要按序执行任务的场景。比如：一些不太重要的收尾，日志等工作可以放到单线程的线程中去执行。
     * 日志记录一般情况会比较慢（数据量大一般可能不写入数据库），顺序执行会拖慢整个接口，堆积更多请求，还可能会对数据库造成影响（事务在开启中），
     * 所以日志记录完全可以扔到单线程的线程中去，一条条的处理，也可以认为是一个单消费者的生产者消费者模式。
     */
    public  static  void newSingleThreadExecutor(){
        // 单一线程池,永远会维护存在一条线程
        ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {
            final int j = i;
            singleThreadPool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + ":" + j);
                }
            });
        }
        singleThreadPool.shutdown();
    }


    /**
     * 4、固定个数的线程池（newScheduledThreadPool）
     * 相比于第一个固定个数的线程池强大在  ①可以执行延时任务，②也可以执行带有返回值的任务
     */
    public  static  void newScheduledThreadPool() throws ExecutionException, InterruptedException {
        // 第四种线程池:固定个数的线程池，相比于第一个固定个数的线程池 强大在 ①可以执行延时任务，②也可以执行
        // 有返回值的任务。
        // scheduledThreadPool.submit(); 执行带有返回值的任务
        // scheduledThreadPool.schedule() 用来执行延时任务.
        // 固定个数的线程池，可以执行延时任务，也可以执行带有返回值的任务。
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
        FutureTask<String> ft = new FutureTask<>(new Callable<String>() {
            @Override
            public String call() throws Exception {
                System.out.println("hello");
                return Thread.currentThread().getName();
            }
        });
        scheduledThreadPool.submit(ft);
        // 通过FutureTask对象获得返回值.
        String result = ft.get();
        System.out.println("result : " + result);

        // 执行延时任务
        scheduledThreadPool.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " : bobm!");
            }
        }, 3L, TimeUnit.SECONDS);

    }

    public static  void main(String[] args) throws ExecutionException, InterruptedException {
        // newFixedThreadPool();
        // newCachedThreadPool();
        // newSingleThreadExecutor();
        // newScheduledThreadPool();
    }
}
